import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosHeaders,
  AxiosHeaderValue,
} from "axios";

// 定义字典接口
interface Dict {
  [key: string]: string; // 更具体的类型
}

// 定义认证配置接口
interface AuthConfig {
  uri: string;
  data: Record<string, unknown>; // 更具体的类型
  tokenKeys: string[];
}

// 定义 AxiosFactory 配置接口
interface AxiosFactoryConfig {
  name: string;
  auth: AuthConfig;
  headers?: Dict;
}

// 自定义错误类
class CustomError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "CustomError";
  }
}

/**
 * AxiosFactory 类用于创建和配置 Axios 实例。
 * 处理登录认证并管理 cookies 和 tokens。
 */
class AxiosFactory {
  cookies: Map<string, string> = new Map();
  tokens: Dict = {};
  config: AxiosFactoryConfig;
  axiosClient: AxiosInstance;
  verified: boolean = false;
  maxCookies: number = 50; // 设置最大 cookie 数量
  retryDelays: number[] = [1000, 5000, 10000, 30000]; // 设置重试延迟时间（毫秒）

  /**
   * 构造函数初始化 AxiosFactory。
   * @param config - AxiosFactory 的配置参数。
   * @param axiosConfig - Axios 的配置参数。
   * @param cache - 缓存配置（目前未使用）。
   */
  constructor(
    config: AxiosFactoryConfig,
    axiosConfig: AxiosRequestConfig,
    cache: any = {}
  ) {
    this.config = config;
    this.axiosClient = this.createAxiosClient(axiosConfig);
  }

  /**
   * 初始化方法，通过调用登录方法进行认证。
   */
  async initialize() {
    await this.login();
  }

  // 登录方法，发送登录请求并处理响应。
  async login(retryAttempt: number = 0): Promise<void> {
    const loginConfig = {
      url: this.config.auth.uri,
      method: "POST",
      data: this.config.auth.data,
    };
    try {
      await this.axiosClient(loginConfig);
      this.verified = true;
      console.log(`httpApp:${this.config.name}初始化成功`);
    } catch (error: any) {
      if ([502, 503, 504].includes(error?.response?.status)) {
        const delay = this.retryDelays[Math.min(retryAttempt, this.retryDelays.length - 1)];
        console.warn(`登录失败，重试第 ${retryAttempt + 1} 次，等待 ${delay / 1000} 秒...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        return this.login(retryAttempt + 1);
      }
      console.error(`httpApp:${this.config.name}初始化失败`, error);
      throw new CustomError(
        `Failed to initialize httpApp: ${this.config.name}`
      );
    }
  }

  /**
   * 创建 Axios 实例并设置拦截器。
   * @param axiosConfig - Axios 的配置参数。
   * @returns 创建的 Axios 实例。
   */
  createAxiosClient(axiosConfig: AxiosRequestConfig): AxiosInstance {
    const config = {
      baseURL: "",
      withCredentials: true,
      timeout: 10000,
      ...axiosConfig,
    };
    const api = axios.create(config);
    this.setupInterceptors(api);
    return api;
  }

  /**
   * 设置请求和响应拦截器。
   * @param api - Axios 实例。
   */
  setupInterceptors(api: AxiosInstance) {
    // 请求拦截器
    api.interceptors.request.use((config) => {
      const headers = new AxiosHeaders();
      headers.set("cookie", Array.from(this.cookies.values()).join(";"));
      if (this.config.headers) {
        Object.entries(this.config.headers).forEach(([key, value]) => {
          headers.set(key, value as AxiosHeaderValue);
        });
      }
      Object.entries(this.tokens).forEach(([key, value]) => {
        headers.set(key, value as AxiosHeaderValue);
      });
      config.headers = headers;
      return config;
    });

    // 响应拦截器
    api.interceptors.response.use(
      (response) => {
        if (response.headers["set-cookie"]) {
          response.headers["set-cookie"].forEach((cookie: string) => {
            const cookieName = cookie.split("=")[0].trim();
            this.cookies.set(cookieName, cookie);
          });

          // 限制 cookie 数量
          if (this.cookies.size > this.maxCookies) {
            const cookiesArray = Array.from(this.cookies.entries());
            this.cookies = new Map(cookiesArray.slice(-this.maxCookies));
          }
        }
        this.config.auth.tokenKeys.forEach((key: string) => {
          if (key in response.headers) {
            this.tokens[key] = response.headers[key];
            console.log(key, this.tokens[key]);
          }
        });
        return response; // 直接返回响应
      },
      async (error) => {
        if (error.response?.status === 401) {
          console.warn(`检测到 401 错误，重新登录...`);
          this.verified = false;
          await this.login();
          return api.request(error.config);
        }
        return Promise.reject(error);
      }
    );
  }
}

export default AxiosFactory;
